﻿Imports System.Net.Sockets
Imports System.ComponentModel
Imports System.Text
Imports System.Runtime.InteropServices
Imports System.Threading
Public Class WebServer

    Public Const TcpTimeout = 1500

    Public server As TcpListener
    Private _TcpConnected As Boolean = False
    Private client As TcpClient

    <Description("是否在工作中")>
    Public ReadOnly Property Listening As Boolean = True

    Public Event OnMessage As Action(Of Byte())

    Public SendQueue As New Queue(Of Byte())

    <Description("TCP/IP连接状态")>
    Public ReadOnly Property TcpConnected As Boolean
        Get
            If client Is Nothing Then
                Return False
            End If
            If client.Client Is Nothing Then
                Return False
            End If
            Return _TcpConnected
        End Get
    End Property

    <Description("Tcp接收缓存数据大小")>
    Public ReadOnly Property BytesToRead As Integer
        Get
            If client Is Nothing Then
                Return 0
            End If

            If client.Client Is Nothing Then
                Return 0
            End If

            Return client.Client.Available
        End Get
    End Property

    <Description("Tcp Server在接收数据中")>
    Public Property ServerReceiving As Boolean

    <Description("Tcp Server在发送数据中")>
    Public Property ServerSending As Boolean

    <Description("轮训接收到的消息的间隔(ms)")>
    Public Property RecevingInterval As UInteger = 10

    '设置为上线状态，配置TcpClient
    Private Sub TcpSetOnline()
        _TcpConnected = True
    End Sub
    '设置为离线状态，释放资源
    Private Sub TcpSetOffline()
        _TcpConnected = False
        If client IsNot Nothing Then
            client.Close()
        End If
    End Sub

    Public Sub Listen(port As Integer)
        ServerReceiving = False
        ServerSending = False
        If server Is Nothing Then
            server = New TcpListener(Net.IPAddress.Parse("0.0.0.0"), port)
        Else
            Console.WriteLine($"Server {server.LocalEndpoint} Already Exists")
            Return
        End If

        Console.WriteLine($"Server {server.LocalEndpoint} Start Listening")
        server.Start()
        _Listening = True
        While _Listening

            Dim cl As TcpClient = server.AcceptTcpClient()
            If TcpConnected Then
                If client IsNot Nothing Then
                    Console.WriteLine($"Only One Connection Can be Established, Current Connection Will be Closed.")
                    TcpSetOffline()
                    Dim tick = Now.Ticks
                    While ServerReceiving Or ServerSending
                        '等待上一个连接实例的接收线程退出
                        If Now.Ticks - tick > 20 Then
                            Console.WriteLine($"Close Time Out. ServerReceiving:{ServerReceiving}, ServerSending:{ServerSending}.")
                            ServerReceiving = False
                            ServerSending = False
                            Exit While
                        End If
                    End While
                    client.Dispose()
                End If
            End If
            client = cl
            ' 设置收发超时
            client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, TcpTimeout)
            client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, TcpTimeout)
            Console.WriteLine($"Connetion Established From {client.Client.RemoteEndPoint}")
            TcpSetOnline()
            Dim t1 = New Thread(AddressOf RecvTask)
            Dim t2 = New Thread(AddressOf SendTask)
            t1.Start()
            t2.Start()
        End While
        _Listening = False
    End Sub

    Public Sub Write(s As String)
        Dim b = Encoding.UTF8.GetBytes(s)
        Write(b)
    End Sub

    Public Sub Write(valSendBuffer As Byte())
        If Not TcpConnected Then Return
        SendQueue.Enqueue(valSendBuffer)
    End Sub

    Public Function ReadByte() As Byte
        Dim b(0) As Byte
        client.Client.Receive(b, 1, SocketFlags.None)
        Return b(0)
    End Function

    Public Function Read(ByRef buffer() As Byte, offset As Integer, count As Integer) As Integer
        Dim ret = client.Client.Receive(buffer, offset, count, SocketFlags.None)
        Return ret
    End Function

    Public Function Read(bytes_to_read As Integer) As String
        Dim b(bytes_to_read - 1) As Byte
        Dim ret As String = ""
        client.Client.Receive(b)
        Try
            ret = Encoding.ASCII.GetString(b)
        Catch ex As Exception
            Console.WriteLine(ex.Message)
        End Try
        Return ret
    End Function

    Public Sub DiscardInBuffer()

        If Not TcpConnected Then Return

        If client.Client.Available > 0 Then
            Dim buf() As Byte
            ReDim buf(client.Client.Available)
            client.Client.Receive(buf)
            Console.WriteLine("[Discard Buffer]")
            PrintBytes(buf)
        End If

    End Sub

    Private Sub SendTask()
        Console.WriteLine($"Server {server.LocalEndpoint} Start Send ...")
        ServerSending = True
        While TcpConnected
            If SendQueue.Count > 0 Then
                Dim d = SendQueue.Dequeue()
                Try
                    client.Client.Send(d)
                Catch ex As Exception
                    Console.WriteLine(ex.Message)
                End Try
            Else
                Thread.Sleep(1)
            End If
        End While
        ServerSending = False
        Console.WriteLine($"Server {server.LocalEndpoint} Send Loop Stopped .")
    End Sub

    Private Sub RecvTask()
        Console.WriteLine($"Server {server.LocalEndpoint} Start Receiving...")
        ServerReceiving = True
        Dim last_bytes = 0
        While TcpConnected
            If BytesToRead = last_bytes And last_bytes <> 0 Then
                Dim buf(last_bytes - 1) As Byte
                Read(buf, 0, last_bytes)
                'PrintBytes(buf)
                RaiseEvent OnMessage(buf)
            End If
            last_bytes = BytesToRead
            Thread.Sleep(RecevingInterval)
        End While
        ServerReceiving = False
        Console.WriteLine($"Server {server.LocalEndpoint} Stopped.")
    End Sub

    ''' <summary>
    '''  使用KeepAlive，一般是客户端要设置的 
    '''  client.Client.IOControl(IOControlCode.KeepAliveValues, KeepAliveOptions(1, 5000, 5000), Nothing)
    ''' </summary>
    ''' <param name="enable">Whether To enable Keep-Alive</param>
    ''' <param name="fisrt_detect">How Long To start the first detection</param>
    ''' <param name="interval">Detection interval</param>
    ''' <returns></returns>
    Public Shared Function KeepAliveOptions(enable As UInteger, fisrt_detect As UInteger, interval As UInteger) As Byte()
        Dim dummy As UInteger = 0
        Dim inOptionValues(Marshal.SizeOf(dummy) * 3 - 1) As Byte
        BitConverter.GetBytes(CUInt(enable)).CopyTo(inOptionValues, 0)
        BitConverter.GetBytes(CUInt(fisrt_detect)).CopyTo(inOptionValues, Marshal.SizeOf(dummy))
        BitConverter.GetBytes(CUInt(interval)).CopyTo(inOptionValues, Marshal.SizeOf(dummy) * 2)
        Return inOptionValues
    End Function

End Class
